預設情況下,Docker 容器與 Pod 內部的檔案系統都是短暫且易逝的(Ephemeral)。
對於 Web 前端或無狀態 API 來說這不是問題;但如果我們要運行的是 MySQL、PostgreSQL、Redis 或使用者上傳的圖片檔案,我們就必須將資料存放在獨立於 Pod 生命週期之外的外部儲存空間。
Kubernetes 為了讓「底層基礎設施維運人員(Admin)」與「應用程式開發者(Developer)」各司其職,設計了一套非常優雅的解耦抽象層:
| 元件 | 角色定位 | 生活比喻 | 誰負責管理 |
|---|---|---|---|
| PV (PersistentVolume) | 實際的實體儲存空間(如 AWS EBS、NFS、本機硬碟) | 實際蓋好的空房子 | 集群管理員 / 雲端平台 |
| PVC (PersistentVolumeClaim) | 開發者提出的一張「儲存需求申請單」(例如:我要 5Gi、讀寫模式) | 租屋者的租屋需求單 | 應用程式開發者 |
| StorageClass (SC) | 自動化房屋仲介:當沒有合適的現成房子(PV)時,自動依需求去雲端「蓋一棟新的房子(Dynamic Provisioning)」 | 房屋仲介與建商 | 集群管理員 |
運作邏輯:開發者只需撰寫一份 PVC,Kubernetes 就會自動尋找符合規格的 PV 進行綁定(Bound),並掛載進 Pod 容器中使用。
當 PVC 被刪除後,背後關聯的實體 PV 資料該如何處理?
在 Minikube 環境中,系統已經內建了一個預設的 standard StorageClass(以 HostPath 為底層)。當我們建立 PVC 時,K8s 會自動幫我們動態建立出對應的 PV!
建立 my-pvc.yaml,申請 1Gi 的空間:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
套用配置並檢查狀態:
kubectl apply -f my-pvc.yaml
kubectl get pvc
預期輸出 STATUS 為 Bound,代表已經成功綁定到一個自動生成的 PV!
建立 writer-pod.yaml,將 PVC 掛載到容器內的 /data 目錄:
apiVersion: v1
kind: Pod
metadata:
name: writer-pod
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: storage-volume
mountPath: /data
volumes:
- name: storage-volume
persistentVolumeClaim:
claimName: demo-pvc
套用配置並在 /data 目錄寫入一行測試文字:
kubectl apply -f writer-pod.yaml
# 進入容器寫入資料到持久化磁碟中
kubectl exec writer-pod -- sh -c "echo 'K8s Data Persistence Success!' > /data/message.txt"
# 驗證資料是否寫入成功
kubectl exec writer-pod -- cat /data/message.txt
現在我們狠心地把 writer-pod 徹底刪除:
kubectl delete pod writer-pod
接著建立一個全新的 reader-pod.yaml,同樣掛載這份 demo-pvc:
apiVersion: v1
kind: Pod
metadata:
name: reader-pod
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: storage-volume
mountPath: /data
volumes:
- name: storage-volume
persistentVolumeClaim:
claimName: demo-pvc
套用並檢查新 Pod 是否能讀到上一代留下來的遺產:
kubectl apply -f reader-pod.yaml
# 查看新 Pod 裡面的檔案內容
kubectl exec reader-pod -- cat /data/message.txt
預期輸出:
K8s Data Persistence Success!
即便舊的 Pod 早已被摧毀,資料依然完好如初地被新 Pod 繼承!
今天我們成功解鎖了資料持久化技能:
有了 PVC 做後盾,我們終於有能力在 Kubernetes 上運行資料庫了!但如果用傳統的 Deployment 跑 MySQL / Redis,在多副本擴展時會遇到「網路識別碼混亂」與「磁區共用衝突」的問題。
明天 Day 17,我們將探討專門管理有狀態資料庫的守護神:「有狀態應用:StatefulSet 如何管理 MySQL / Redis 等資料庫?」!